Skip to content

chore(git): sync with main - #31331

Merged
ShaneK merged 31 commits into
nextfrom
sync-next-with-main
Aug 6, 2026
Merged

chore(git): sync with main#31331
ShaneK merged 31 commits into
nextfrom
sync-next-with-main

Conversation

@ShaneK

@ShaneK ShaneK commented Aug 5, 2026

Copy link
Copy Markdown
Member

Sync next with main.

renovate Bot and others added 28 commits July 23, 2026 13:30
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[github/codeql-action](https://redirect.github.com/github/codeql-action)
| action | patch | `v4.37.1` → `v4.37.3` |

---

### Release Notes

<details>
<summary>github/codeql-action (github/codeql-action)</summary>

###
[`v4.37.3`](https://redirect.github.com/github/codeql-action/releases/tag/v4.37.3)

[Compare
Source](https://redirect.github.com/github/codeql-action/compare/v4.37.2...v4.37.3)

No user facing changes.

###
[`v4.37.2`](https://redirect.github.com/github/codeql-action/releases/tag/v4.37.2)

[Compare
Source](https://redirect.github.com/github/codeql-action/compare/v4.37.1...v4.37.2)

- The new address format for the `config-file` input that was introduced
in CodeQL Action 4.37.0 is now enabled by default. In addition to the
format described there, the `remote=` prefix can now be used to
explicitly indicate that the input refers to a remote file. All previous
input formats continue to be accepted as well.
[#&#8203;4023](https://redirect.github.com/github/codeql-action/pull/4023)
- The CodeQL Action can now make use of [configured private
registries](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries)
in Default Setup to retrieve CodeQL configuration files from remote
repositories that require authentication. This will allow customers to
store their CodeQL configuration in a single repository that can then be
referenced by Default Setup workflows in other repositories. We expect
to roll this and other, related changes out to everyone in July.
[#&#8203;4007](https://redirect.github.com/github/codeql-action/pull/4007)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzUuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI3NS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Issue number: internal

---------

## What is the current behavior?
When a sheet modal uses `handleBehavior="cycle"`, the host element is
focusable (`tabIndex=0`) and `onModalFocus` redirects focus to the drag
handle whenever the host is focused. `present()` moves focus to the
`.modal-wrapper` (the `role="dialog"` element) inside the shadow DOM,
but that focus event is retargeted to the host, so `onModalFocus` sees
`ev.target === el` and treats it as a direct host focus. It then bounces
focus onto the handle.

This is latent on the default `handleBehavior="none"` (the host isn't
focusable, so the redirect never runs), but reproduces on any sheet
modal that opts into `cycle`.

## What is the new behavior?
`onModalFocus` now redirects to the handle only when the host itself was
focused directly, detected by `el.shadowRoot?.activeElement` being
`null`. When `present()` focuses the dialog wrapper, `activeElement` is
the wrapper (not null), so the redirect is skipped and the dialog keeps
focus. Tabbing into the modal from outside still lands on the handle,
since the host is the focused element in that case.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

## Other information
Related to the dialog focus work in #31260 . That change is correct
under the default `handleBehavior`, but the `cycle` path was not covered
until now. Adds an e2e test to `utils/test/overlays/overlays.e2e.ts`
that presents a sheet modal with `handle-behavior="cycle"` and asserts
focus stays on the wrapper.

The same fix ships on the major-9.0 sync (#31290), where `cycle` is the
default and this bug is hit on every sheet modal.

Preview (sheet modal test page):
- iOS:
https://ionic-framework-git-fix-modal-focus-cycle-ionic1.vercel.app/src/components/modal/test/sheet?ionic:mode=ios
- MD:
https://ionic-framework-git-fix-modal-focus-cycle-ionic1.vercel.app/src/components/modal/test/sheet?ionic:mode=md
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [chalk](https://redirect.github.com/chalk/chalk) | [`^5.3.0` →
`^6.0.0`](https://renovatebot.com/diffs/npm/chalk/5.6.2/6.0.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/chalk/6.0.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/chalk/5.6.2/6.0.0?slim=true)
|

---

### Release Notes

<details>
<summary>chalk/chalk (chalk)</summary>

###
[`v6.0.0`](https://redirect.github.com/chalk/chalk/compare/v5.6.2...661317e6f91fe7c90306c2c48ea9354562ee9146)

[Compare
Source](https://redirect.github.com/chalk/chalk/compare/v5.6.2...v6.0.0)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…when focused (#31276)

Issue number: resolves #30401

---------
## What is the current behavior?
The floating label and placeholder on `ion-select` overlap when focused
without a value

## What is the new behavior?
The placeholder is not shown when a floating label select is focused, as
that space is taken up by the label

## Does this introduce a breaking change?

- [ ] Yes
- [x] No
Issue number: internal

---------

## What is the current behavior?

The shared `dragElementBy` Playwright helper
(`core/src/utils/test/playwright/drag-element.ts`, used by every gesture
e2e test) unconditionally runs `await page.evaluate(() =>
window.getSelection()?.removeAllRanges())` between `mouse.down()` and
the drag movement. That line was added in #31260 to clear a Firefox-only
text selection, but the extra `page.evaluate()` is an awaited round-trip
that injects variable latency into the gesture path for all browsers. On
WebKit that latency messes up the gesture's timing-derived `velocityX`,
so the `item-sliding` safe-area screenshot tests in md mode settle a few
pixels off and flake randomly. Since it lives on main, the flake has
propagated to every other branch (next and major-9.0) and causes random
test failures in PRs and nightlies.

## What is the new behavior?

The selection clear now only runs on Firefox, which is the only engine
that needs it (the existing comment already scoped the problem to
Firefox). Chromium and WebKit skip the extra round-trip and get the same
drag timing they had before #31260, so the item settles
deterministically and the flake goes away. This matches the
`browserType().name() === 'webkit'` gate already used elsewhere in the
same file.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Regression was introduced by #31260 (the `removeAllRanges` line, not the
modal a11y change itself). Verified by running in docker locally several
times.
Issue number: resolves #31296

---------

## What is the current behavior?

`StackController.destroy()` sets `this.containerEl = undefined!`, but a
transition that is still queued behind `wait()` can resolve after the
`ion-router-outlet` was destroyed (interrupted / re-entrant / cancelled
navigation during fast back-navigation). `transition()` then
dereferences the `undefined` element at `if ((containerEl as
any).commit)` and throws:

```
TypeError: undefined is not an object (evaluating 'containerEl.commit')
    at transition
```

Observed at production scale in an Ionic Angular + Capacitor app
(several hundred RUM crash events per week from a single build,
concentrated on rapid back-navigation between nested outlets).

## What is the new behavior?

- `transition()` null-checks `containerEl` (optional chaining) before
probing for `commit`.
- A transition resolving after its outlet was destroyed now takes the
method's existing fallback path and resolves `false`, instead of
throwing an unhandled `TypeError`.
- No behavior change for live outlets.

Regarding tests: there is no existing unit harness for `StackController`
(only the e2e test apps), and the defect is a timing race between a
queued transition and outlet destruction, which is impractical to
reproduce deterministically in e2e. The change is a defensive null-check
on a field that `destroy()` explicitly sets to `undefined` (with a
lint-suppressed non-null assertion). We have run this exact guard in
production via patch-package: the crash class disappears with no
observed regressions.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

## Other information

The unguarded dereference is present in v8.8.13 through latest stable
8.8.15 and `8.8.16-nightly.20260724`.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update |
|---|---|---|---|---|---|
| [@playwright/test](https://playwright.dev)
([source](https://redirect.github.com/microsoft/playwright)) |
[`^1.61.1` →
`^1.62.0`](https://renovatebot.com/diffs/npm/@playwright%2ftest/1.61.1/1.62.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@playwright%2ftest/1.62.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@playwright%2ftest/1.61.1/1.62.0?slim=true)
| devDependencies | minor |
| mcr.microsoft.com/playwright | `v1.61.1` → `v1.62.0` |
![age](https://developer.mend.io/api/mc/badges/age/docker/mcr.microsoft.com%2fplaywright/v1.62.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/docker/mcr.microsoft.com%2fplaywright/v1.61.1/v1.62.0?slim=true)
| final | minor |
| [playwright-core](https://playwright.dev)
([source](https://redirect.github.com/microsoft/playwright)) |
[`^1.61.1` →
`^1.62.0`](https://renovatebot.com/diffs/npm/playwright-core/1.61.1/1.62.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/playwright-core/1.62.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/playwright-core/1.61.1/1.62.0?slim=true)
| devDependencies | minor |

---

### Release Notes

<details>
<summary>microsoft/playwright (@&#8203;playwright/test)</summary>

###
[`v1.62.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.62.0)

[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.61.1...v1.62.0)

##### 🧱 New component testing model

[Component testing](https://playwright.dev/docs/test-components) moves
to a **stories and galleries** model.
A **story** wraps your component in one specific scenario — hard-coded
props, mock data, providers — and a

**gallery** page that you serve renders stories on demand. The new
[fixtures.mount()](https://playwright.dev/docs/api/class-fixtures#fixtures-mount)
fixture navigates
to the gallery, mounts a story by id, and returns a
[Locator](https://playwright.dev/docs/api/class-locator) scoped to the
story's root element:

```js
test('click should expand', async ({ mount }) => {
  const component = await mount('components/Expandable/Stateful');
  await component.getByRole('button').click();
  await expect(component.getByTestId('expanded')).toHaveValue('true');
});
```

Pass a story type as a template argument to type-check its props, and
use `update(props)` /
`unmount()` on the returned locator to re-render or tear down within a
test.

##### 🛑 Cancel operations with AbortSignal

Most operations and web-first assertions now accept a `signal` option
that takes an

[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal),
letting you
cancel long-running actions, navigations, waits, and assertions:

```js
const controller = new AbortController();
setTimeout(() => controller.abort(), 1000);

await page.getByRole('button', { name: 'Submit' }).click({ signal: controller.signal });
await expect(page.getByText('Done')).toBeVisible({ signal: controller.signal });
```

Providing a signal does not disable the default timeout; pass `timeout:
0` to disable it.

##### 🖼️ WebP screenshots


[expect(page).toHaveScreenshot()](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1)
and
[expect(locator).toHaveScreenshot()](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-screenshot-1)
can now store snapshots in the WebP format — just give the snapshot a
`.webp` name:

```js
// Visual comparisons store the golden snapshot as lossless WebP.
await expect(page).toHaveScreenshot('homepage.webp');

// Standalone screenshots can trade quality for size with lossy WebP.
await page.screenshot({ path: 'homepage.webp', quality: 50 });
```


[page.screenshot()](https://playwright.dev/docs/api/class-page#page-screenshot)
and
[locator.screenshot()](https://playwright.dev/docs/api/class-locator#locator-screenshot)
also accept `webp` as a `type`,
where quality `100` (the default) is lossless and lower values use lossy
compression.

##### 🧩 Custom test filtering with Reporter.preprocess()

New
[reporter.preprocess()](https://playwright.dev/docs/api/class-reporter#reporter-preprocess)
hook runs after the configuration is resolved and before

[reporter.onBegin()](https://playwright.dev/docs/api/class-reporter#reporter-on-begin),
letting a reporter mark individual tests as skipped, excluded,
fixed, or failing through a
[TestRun](https://playwright.dev/docs/api/class-testrun) object:

```js
class MyReporter {
  async preprocess({ config, suite, testRun }) {
    for (const test of suite.allTests()) {
      if (shouldSkip(test))
        testRun.skip(test);
    }
  }
}
```

##### 🔁 Isolated retries

New
[testConfig.retryStrategy](https://playwright.dev/docs/api/class-testconfig#test-config-retry-strategy)
controls when failed tests are retried. The default
`'immediate'` retries as soon as a worker is free; `'isolated'` runs all
retries at the end,
one by one in a single worker, to minimize interference with the rest of
the suite:

```js
// playwright.config.ts
export default defineConfig({
  retries: 2,
  retryStrategy: 'isolated',
});
```

##### New APIs

##### Browser and Context

- New option
[`credentials`](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state-option-credentials)
includes the context's virtual WebAuthn
[Credentials](https://playwright.dev/docs/api/class-credentials)
(passkeys) in the storage state, so they can be persisted and re-seeded
into later contexts.

##### Actions

- New `scroll` option (`"auto"` | `"none"`) on actions to opt out of
Playwright's automatic scroll-into-view.

##### Network

- New
[apiResponse.timing()](https://playwright.dev/docs/api/class-apiresponse#api-response-timing)
returns resource timing information for an API response.

##### Evaluation

- New
[locator.waitForFunction()](https://playwright.dev/docs/api/class-locator#locator-wait-for-function)
waits until a function — called with the matching element — returns a
truthy value.
-
[page.evaluate()](https://playwright.dev/docs/api/class-page#page-evaluate)
and related methods now accept functions as evaluate arguments.
-
[page.addInitScript()](https://playwright.dev/docs/api/class-page#page-add-init-script)
/
[browserContext.addInitScript()](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script)
now accept functions as init-script arguments.

##### Command line & MCP

- Playwright now bundles the [Playwright
MCP](https://playwright.dev/docs/getting-started-mcp) server and
[`playwright-cli`](https://playwright.dev/docs/getting-started-cli),
runnable via `npx playwright mcp` and `npx playwright cli`.

##### Reporters

- The HTML report's **Merge files** grouping — previously only a UI
toggle — can now be enabled from the config with the new `mergeFiles`
reporter option:

```js
// playwright.config.ts
export default defineConfig({
  reporter: [['html', { mergeFiles: true }]],
});
```

##### Announcements

- ⚠️ Debian 11 is not supported anymore.

##### Browser Versions

- Chromium 151.0.7922.34
- Mozilla Firefox 153.0
- WebKit 26.5

This version was also tested against the following stable channels:

- Google Chrome 151
- Microsoft Edge 151

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ionitron <hi@ionicframework.com>
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[github/codeql-action](https://redirect.github.com/github/codeql-action)
| action | patch | `v4.37.3` → `v4.37.4` |

---

### Release Notes

<details>
<summary>github/codeql-action (github/codeql-action)</summary>

###
[`v4.37.4`](https://redirect.github.com/github/codeql-action/compare/v4.37.3...v4.37.4)

[Compare
Source](https://redirect.github.com/github/codeql-action/compare/v4.37.3...v4.37.4)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Updates Docker instructions to include the required Virtual Machine
settings for Rancher Desktop.

Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/labeler](https://redirect.github.com/actions/labeler) |
action | major | `v6.2.0` → `v7.0.0` |

---

### Release Notes

<details>
<summary>actions/labeler (actions/labeler)</summary>

###
[`v7.0.0`](https://redirect.github.com/actions/labeler/compare/v6.2.0...v7.0.0)

[Compare
Source](https://redirect.github.com/actions/labeler/compare/v7.0.0...v7.0.0)

###
[`v7`](https://redirect.github.com/actions/labeler/compare/v6.2.0...v7.0.0)

[Compare
Source](https://redirect.github.com/actions/labeler/compare/v6.2.0...v7.0.0)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzIuNCIsInVwZGF0ZWRJblZlciI6IjQ0LjMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | patch | `v7.0.0` → `v7.0.1` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

###
[`v7.0.1`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v701)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v7.0.0...v7.0.1)

- Bump github/codeql-action from 3 to 4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2475](https://redirect.github.com/actions/checkout/pull/2475)
- Bump actions/setup-node from 4 to 6 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2477](https://redirect.github.com/actions/checkout/pull/2477)
- Bump docker/build-push-action from 6.5.0 to 7.2.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2478](https://redirect.github.com/actions/checkout/pull/2478)
- Bump docker/login-action from 3.3.0 to 4.2.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2479](https://redirect.github.com/actions/checkout/pull/2479)
- Bump actions/checkout from 6 to 7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2488](https://redirect.github.com/actions/checkout/pull/2488)
- Bump actions/upload-artifact from 4 to 7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2476](https://redirect.github.com/actions/checkout/pull/2476)
- eslint 9 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2474](https://redirect.github.com/actions/checkout/pull/2474)
- Bump the minor-actions-dependencies group with 2 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2499](https://redirect.github.com/actions/checkout/pull/2499)
- skip running unsafe pr check if input is default by
[@&#8203;aiqiaoy](https://redirect.github.com/aiqiaoy) in
[#&#8203;2518](https://redirect.github.com/actions/checkout/pull/2518)
- trim only ascii whitespace for branch by
[@&#8203;aiqiaoy](https://redirect.github.com/aiqiaoy) in
[#&#8203;2521](https://redirect.github.com/actions/checkout/pull/2521)
- escape values passed to --unset by
[@&#8203;aiqiaoy](https://redirect.github.com/aiqiaoy) in
[#&#8203;2530](https://redirect.github.com/actions/checkout/pull/2530)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzIuNCIsInVwZGF0ZWRJblZlciI6IjQ0LjMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update |
|---|---|---|---|---|---|
| [@playwright/test](https://playwright.dev)
([source](https://redirect.github.com/microsoft/playwright)) |
[`^1.62.0` →
`^1.62.1`](https://renovatebot.com/diffs/npm/@playwright%2ftest/1.62.0/1.62.1)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@playwright%2ftest/1.62.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@playwright%2ftest/1.62.0/1.62.1?slim=true)
| devDependencies | patch |
| mcr.microsoft.com/playwright | `v1.62.0` → `v1.62.1` |
![age](https://developer.mend.io/api/mc/badges/age/docker/mcr.microsoft.com%2fplaywright/v1.62.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/docker/mcr.microsoft.com%2fplaywright/v1.62.0/v1.62.1?slim=true)
| final | patch |
| [playwright-core](https://playwright.dev)
([source](https://redirect.github.com/microsoft/playwright)) |
[`^1.62.0` →
`^1.62.1`](https://renovatebot.com/diffs/npm/playwright-core/1.62.0/1.62.1)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/playwright-core/1.62.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/playwright-core/1.62.0/1.62.1?slim=true)
| devDependencies | patch |

---

### Release Notes

<details>
<summary>microsoft/playwright (@&#8203;playwright/test)</summary>

###
[`v1.62.1`](https://redirect.github.com/microsoft/playwright/compare/v1.62.0...26a9e470a7b3c7822084b09fb7f13902c5f37b51)

[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.62.0...v1.62.1)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…1295)

Issue number: internal

---------

## What is the current behavior?

Currently, `ion-checkbox` and `ion-toggle` never show a keyboard focus
indicator. `ion-checkbox` doesn't set the `ion-focusable` class and has
no focus styles in the `ios` or `md` themes, so it can't receive the
`ion-focused` class or render a ring. `ion-toggle`'s only focus rule
(`:host(.ion-focused) input`) targets the native `input`, which is
`display: none`, so it never renders.

Separately, `ion-checkbox`, `ion-radio`, and `ion-toggle` show no focus
indicator inside an `ion-item` that holds more than one input.
`ion-checkbox` and `ion-radio` disable their own indicator in an item on
the assumption the item draws one, but a multi-input item has no cover
to highlight, so nothing is focused.

## What is the new behavior?

`ion-checkbox` now sets `ion-focusable` when it isn't deferring to an
item, with matching `ios` and `md` focus styles, so a standalone
checkbox shows the same indicator `ion-radio` already did. Both
`ion-checkbox` and `ion-radio` also become focusable inside a
multi-input item, detected through the item's `item-multiple-inputs`
class, so each control shows its own indicator when the item can't.

`ion-toggle` always shows its own indicator. Unlike checkbox and radio,
it's excluded from the item's input cover, so an item never highlights
on its behalf, in or out of a multi-input item. Its dead `input` focus
rule is replaced with a ring on the visible track in both themes.

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

## Other information

Preview checkbox:
- iOS:
https://ionic-framework-git-fw-7585-fw-7586-ionic1.vercel.app/src/components/checkbox/test/basic?ionic:mode=ios
- MD:
https://ionic-framework-git-fw-7585-fw-7586-ionic1.vercel.app/src/components/checkbox/test/basic?ionic:mode=md

Preview toggle:
-
https://ionic-framework-git-fw-7585-fw-7586-ionic1.vercel.app/src/components/toggle/test/basic?ionic:mode=ios
-
https://ionic-framework-git-fw-7585-fw-7586-ionic1.vercel.app/src/components/toggle/test/basic?ionic:mode=md

Preview radio:
-
https://ionic-framework-git-fw-7585-fw-7586-ionic1.vercel.app/src/components/radio/test/basic?ionic:mode=ios
-
https://ionic-framework-git-fw-7585-fw-7586-ionic1.vercel.app/src/components/radio/test/basic?ionic:mode=md

---------

Co-authored-by: ionitron <hi@ionicframework.com>
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@capacitor/core](https://capacitorjs.com)
([source](https://redirect.github.com/ionic-team/capacitor)) | [`8.4.2`
→
`8.5.0`](https://renovatebot.com/diffs/npm/@capacitor%2fcore/8.4.2/8.5.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@capacitor%2fcore/8.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@capacitor%2fcore/8.4.2/8.5.0?slim=true)
|

---

### Release Notes

<details>
<summary>ionic-team/capacitor (@&#8203;capacitor/core)</summary>

###
[`v8.5.0`](https://redirect.github.com/ionic-team/capacitor/blob/HEAD/CHANGELOG.md#850-2026-07-31)

[Compare
Source](https://redirect.github.com/ionic-team/capacitor/compare/8.4.2...8.5.0)

##### Bug Fixes

- **cli:** support TypeScript 7 when loading capacitor.config.ts
([#&#8203;8534](https://redirect.github.com/ionic-team/capacitor/issues/8534))
([4c1c870](https://redirect.github.com/ionic-team/capacitor/commit/4c1c8709413b9c19b008c99122ca330cc3c90e6f))

##### Features

- **cli:** add migrator functionality for adopting UIScene
([#&#8203;8544](https://redirect.github.com/ionic-team/capacitor/issues/8544))
([984fa85](https://redirect.github.com/ionic-team/capacitor/commit/984fa85ba0adab0aacf895aed6323bf4b503dccb))
- **ios:** UIScene Support
([#&#8203;8536](https://redirect.github.com/ionic-team/capacitor/issues/8536))
([3fa04a3](https://redirect.github.com/ionic-team/capacitor/commit/3fa04a357c92af34cd6fccb8124791963804a9dc))

####
[8.4.2](https://redirect.github.com/ionic-team/capacitor/compare/8.4.1...8.4.2)
(2026-07-14)

##### Bug Fixes

- **android:** explicitly grant URI permissions for image capture intent
([#&#8203;8526](https://redirect.github.com/ionic-team/capacitor/issues/8526))
([6f2d328](https://redirect.github.com/ionic-team/capacitor/commit/6f2d3283897a375d09ca9ec8784a9f0d65f0a530))

####
[8.4.1](https://redirect.github.com/ionic-team/capacitor/compare/8.4.0...8.4.1)
(2026-06-19)

##### Bug Fixes

- **cli:** make SPM dependency patch work on prereleases
([#&#8203;8508](https://redirect.github.com/ionic-team/capacitor/issues/8508))
([6048e90](https://redirect.github.com/ionic-team/capacitor/commit/6048e90171afa0229a3c25b52a23c377c6bb804c))
- **cli:** patch Capacitor SPM dependency version in plugins
([#&#8203;8492](https://redirect.github.com/ionic-team/capacitor/issues/8492))
([28bb2c6](https://redirect.github.com/ionic-team/capacitor/commit/28bb2c687069dfdd6aa7abc866004a1c6388d103))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "every weekday before 11am"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/ionic-team/ionic-framework).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Issue number: resolves #31315 
---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

When an `ion-refresher`'s host page is unmounted while the native
refresher is still being set up, Ionic throws an uncaught `TypeError`
from the gesture utility:

```
TypeError: Cannot read properties of undefined (reading '__zone_symbol__addEventListener')
    at addEventListener (core/src/utils/gesture/listener.ts:21)
    at Object.enable (core/src/utils/gesture/index.ts)
    at Refresher.disabledChanged (core/src/components/refresher/refresher.tsx:125)
    at Refresher.setupMDNativeRefresher (core/src/components/refresher/refresher.tsx:372)
```

`setupMDNativeRefresher()` and `setupiOSNativeRefresher()` both do:

```ts
this.gesture = (await import('../../utils/gesture')).createGesture({
  el: this.scrollEl!,
  ...
});
```

The dynamic import yields to the event loop. If the refresher is
disconnected while it resolves, `disconnectedCallback()` (line 547-549)
has already run `this.scrollEl = undefined`, so `createGesture` receives
`el: undefined`, and the subsequent `disabledChanged()` →
`gesture.enable(true)` calls `addEventListener(undefined, ...)`, which
throws.

Because this happens inside an async method that nothing awaits, it
surfaces as an **unhandled promise rejection** — it cannot be caught by
a framework error boundary; this crashes my app in playwright sometimes
when components are mounted very quickly.

The `!` non-null assertions on `this.scrollEl` are what hide this from
TypeScript.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

If the refresher is disconnected while the gesture module is being
imported, setup aborts quietly. No gesture is created, and no error is
thrown.

## Does this introduce a breaking change?

- [ ] Yes
- [ X] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

---------

Co-authored-by: Shane <shane@shanessite.net>
…ken contributing link (#31250)

Issue number: resolves #31251

---------

## What is the current behavior?

While working on e2e tests in the angular directory, I encountered a few
documentation issues that made the local development workflow more
difficult to follow.

- The **"See Ionic's E2E testing guide"** link in `CONTRIBUTING.md`
resolves to a 404 when viewed from GitHub's **Contributing** tab because
the relative path is not resolved correctly.
- Broken link:
https://github.com/ionic-team/ionic-framework/blob/core/src/utils/test/playwright/docs/README.md
- Intended destination:
https://github.com/ionic-team/ionic-framework/blob/main/core/src/utils/test/playwright/docs/README.md

- The Angular testing guide (`docs/angular/testing.md`) omits several
steps that are necessary to successfully test local framework changes,
including:
- Removing existing `npm link` relationships before starting a new sync
cycle.
  - Building `packages/angular-server`.
- Installing Playwright browser dependencies before running Playwright
tests for the first time.

These omissions made it difficult to reproduce and validate additions to
Angular tests I was adding as part of a separate PR.

## What is the new behavior?

This PR does the following:

- Fixes the broken link to the Ionic E2E testing guide in
`CONTRIBUTING.md` by using a repository-root-relative path.
- Updating `docs/angular/testing.md` to document the complete workflow
for syncing local Angular framework changes.
- Documents that `packages/angular-server` should be built as part of
the local testing workflow.
- Documents that contributors should remove previous `npm link`
relationships before beginning a new sync cycle.
- Clarifies the recommended workflow for syncing local package changes.
- Documents the Playwright browser installation step required before
running Playwright tests for the first time.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

## Other information

---------

Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
Issue number: resolves #31152

## What is the current behavior?

In `IonRouter.handleHistoryChange`, the URL-change guard compares
mismatched operands:

```ts
const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search;
if (leavingUrl !== location.pathname) { ... }
```

The left side includes `search`, the right side does not. For any route
with a non-empty query string, the comparison is **always** unequal, so
the transition block runs on every history event — including no-op
popstates over same-URL entries pushed via `window.history.pushState`.

Concretely: when a same-URL history entry on a search-bearing route is
popped, `action === 'POP'` is processed and the IRO transitions to
`currentRoute.pushedByRoute`. The browser URL doesn't change but the
rendered view does — the user gets silently teleported to a different
page in the stack.

Minimal repro: on any search-bearing route, run in the console:
```js
window.history.pushState({}, '', window.location.href);
window.history.back();
```
The current behavior swaps the rendered page to the previous entry in
`locationHistory` while the URL stays put. Expected: no visible change.

Full repro and analysis in #31152. This is also the root cause behind
the symptom reported in #25534 (framed there as a transition-rerender
flash).

## What is the new behavior?

The right side of the comparison now also includes `search`, so the
block only runs when the URL actually changed:

```ts
const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search;
const currentUrl = location.pathname + (location.search || '');
if (leavingUrl !== currentUrl) { ... }
```

Behavior matrix:
- Search-only navigations (e.g. `routerPush(samePath + newSearch)`):
**unchanged** — pathname matches but search differs, block still runs.
- Pathname changes: **unchanged** — pathnames differ, block still runs.
- No-op popstates on search-bearing routes: **fixed** — pathname +
search both match, block correctly skipped.

A new Cypress regression test is included
(`packages/react-router/test/base/tests/e2e/specs/routing.cy.js`) that
pushes a same-URL state on a search-bearing route, calls
`history.back()`, and asserts the page does not teleport.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

The comparison becomes stricter (skips the block in more cases than
before), but only for the cases where the URL didn't actually change.
All cases where the URL *did* change are still routed through the
existing transition logic unchanged.

## Other information

Happy to iterate on the fix shape if there's a preferred alternative —
e.g. gating behind a flag for backward compat, or restructuring the
guard differently. The one-line change above is the smallest possible
fix that keeps existing behavior for every "real URL change" case.

---------

Co-authored-by: ShaneK <shane@shanessite.net>
…ty (#31249)

## Summary

Fixes #18550 — `ion-fab-button` with `type="submit"` does not submit its
parent form.

**Root cause:** `ion-fab-button` uses Shadow DOM, so its internal
`<button>` element is not part of the form-associated elements in the
light DOM. Clicking it (or pressing Enter in a text field) never
triggers the form's `submit` event.

**Fix:** Port the hidden-button pattern already used by `ion-button`:
- On render (when `type !== 'button'`), inject a hidden `<button>` into
the associated form in the light DOM.
- On click, call `ev.preventDefault()` to stop the shadow button from
acting, then `.click()` the hidden button — which is a proper form
participant and triggers submission.
- Add a `form` prop (`string | HTMLFormElement`) so the button can
target a form outside its DOM subtree (same API as `ion-button`).
- Keep the `disabled` watcher in sync with the hidden button so a
disabled fab-button cannot submit.

## Changes

- `core/src/components/fab-button/fab-button.tsx` — core fix
- `core/src/components/fab-button/test/form/fab-button.e2e.ts` —
Playwright e2e tests (closest form, form by id, form by reference, Enter
key, disabled, reset)
- `core/src/components/fab-button/test/form/fab-button.spec.ts` — unit
test asserting the hidden button is not duplicated on re-renders
- `core/src/components/fab-button/test/form/index.html` — visual test
page

## Test plan

- [ ] `npm run test.spec` passes (unit test for hidden-button dedup)
- [ ] `npm run test.e2e` passes for
`fab-button/test/form/fab-button.e2e.ts`
- [ ] Manual: `ion-fab-button type="submit"` inside a `<form>` submits
on click
- [ ] Manual: pressing Enter in a form input triggers submit
- [ ] Manual: `ion-fab-button type="reset"` resets form fields
- [ ] Manual: disabled fab-button does not submit
- [ ] Manual: `form="id"` targets an external form

---------

Co-authored-by: ShaneK <shane@shanessite.net>
Issue number: internal

---------

## What is the current behavior?

Currently, `prepareLazyLoaded` in `ion-tab` sets `loaded = true` before
it attempts the attach, and the `try/catch` wrapped around
`attachComponent` is synchronous while `attachComponent` itself is
`async`. So when the first attach rejects, the rejection skips the
`catch` entirely and `loaded` is already `true`, which means the `if
(!this.loaded && ...)` guard blocks every later attempt. The tab renders
as an empty page for the rest of the session, even if whatever caused
the failure is fixed.

`ion-tabs` activates a tab by setting `active` rather than by awaiting
the tab's `setActive()`, so the failure goes through the
`@Watch('active')` path. That call isn't awaited, so on rejection the
error surfaces as an unhandled promise rejection and `printIonError`
never runs. `select()` resolves successfully while the tab is blank, so
there's no signal to the caller either.

## What is the new behavior?

With this change, `prepareLazyLoaded` caches the in-flight attach
promise instead of flipping a boolean up front. Concurrent activations
still share a single attempt, which is what the old `loaded = true`
ordering was providing, but the cache is only cleared when the attach
rejects, so the next activation retries. The unawaited call in the
`active` watcher now has a `.catch` that routes the error to
`printIonError`, which is what the unreachable `catch` block was there
for.

`setActive()` still rejects on a failed attach and still leaves `active`
as `false`, so that part is unchanged.

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

## Other information

There's no stock test page that reproduces the failure, since it needs a
delegate that rejects, but the tabs pages confirm normal lazy loading
still works:

- [Tabs:
Basic](https://ionic-framework-git-fw-7296-ionic1.vercel.app/src/components/tabs/test/basic)
# Conflicts:
#	core/package-lock.json
#	core/package.json
#	core/src/components/alert/test/a11y/alert.e2e.ts-snapshots/alert-checkbox-scale-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/alert/test/a11y/alert.e2e.ts-snapshots/alert-radio-scale-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/alert/test/basic/alert.e2e.ts-snapshots/alert-multipleButtons-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/alert/test/basic/alert.e2e.ts-snapshots/alert-multipleButtons-md-ltr-dark-Mobile-Chrome-linux.png
#	core/src/components/alert/test/basic/alert.e2e.ts-snapshots/alert-multipleButtons-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/alert/test/basic/alert.e2e.ts-snapshots/alert-multipleButtons-md-rtl-dark-Mobile-Chrome-linux.png
#	core/src/components/button/test/icon/button.e2e.ts-snapshots/button-icon-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/icon/button.e2e.ts-snapshots/button-icon-ios-rtl-Mobile-Chrome-linux.png
#	core/src/components/checkbox/checkbox.common.scss
#	core/src/components/checkbox/checkbox.tsx
#	core/src/components/checkbox/checkbox.vars.scss
#	core/src/components/checkbox/test/basic/checkbox.e2e.ts
#	core/src/components/checkbox/test/basic/checkbox.e2e.ts-snapshots/checkbox-focus-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/checkbox/test/basic/checkbox.e2e.ts-snapshots/checkbox-focus-ios-ltr-Mobile-Firefox-linux.png
#	core/src/components/checkbox/test/basic/checkbox.e2e.ts-snapshots/checkbox-focus-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/checkbox/test/basic/checkbox.e2e.ts-snapshots/checkbox-in-item-focus-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/checkbox/test/basic/checkbox.e2e.ts-snapshots/checkbox-in-item-focus-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/fab-button/fab-button.tsx
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-ios-rtl-Mobile-Chrome-linux.png
#	core/src/components/item/test/colors/item.e2e.ts-snapshots/item-colors-diff-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/radio/radio.common.scss
#	core/src/components/radio/radio.ios.vars.scss
#	core/src/components/radio/radio.md.vars.scss
#	core/src/components/radio/radio.native.vars.scss
#	core/src/components/radio/test/basic/radio.e2e.ts
#	core/src/components/spinner/test/color/spinner.e2e.ts-snapshots/spinner-color-diff-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/toggle/toggle.common.scss
#	core/src/components/toggle/toggle.tsx
#	core/src/components/toggle/toggle.vars.scss
#	packages/angular/src/directives/proxies.ts
#	packages/angular/standalone/src/directives/proxies.ts
The conflicting snapshots changed on main only from the playwright 1.62.1
bump, while next changed them from real component rendering work. Carry
next's baselines forward; the checkbox focus snapshots stay on main's
because #31295 is the code landing here.
@ShaneK
ShaneK requested a review from a team as a code owner August 5, 2026 19:33
@ShaneK
ShaneK requested a review from BenOsodrac August 5, 2026 19:33
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview Aug 6, 2026 1:23pm

Request Review

@github-actions github-actions Bot added package: core @ionic/core package package: angular @ionic/angular package package: vue @ionic/vue package package: react @ionic/react package labels Aug 5, 2026
…ents

The keyboard focus indicator is drawn 7px outside the control, so it
widened the hit area and swallowed clicks aimed just past the control.
A checkbox clicked below itself kept focus instead of blurring, so the
next click emitted no ionFocus.

This only surfaced on next: main starts the focus-visible utility in
ion-app, so a setContent test without one never got ion-focused and
never drew the ring. next initializes it globally (#31165), so the ring
main added in #31295 now renders in those tests too.

The indicator is decorative and must never take pointer events. The ios
toggle uses box-shadow, which cannot capture them, so it needs no change.
The snapshot update run captured the md/Mobile Safari modal before the
datetime calendar body rendered, so the regenerated baseline has an empty
day grid. Restore next's baseline.

The calendar is driven by an IntersectionObserver and the test only waits
for ionModalDidPresent, so a loaded Linux WebKit shard can screenshot
before the grid lands. Nothing in this merge touches datetime: the other
five browser/mode baselines for the same test were unchanged, and the
grid populates 15/15 locally on WebKit.
@ShaneK
ShaneK merged commit ed18ee9 into next Aug 6, 2026
49 checks passed
@ShaneK
ShaneK deleted the sync-next-with-main branch August 6, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: angular @ionic/angular package package: core @ionic/core package package: react @ionic/react package package: vue @ionic/vue package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants